%code requires {
	#include "TablicaSymboli.h"
	#include "Quadruples.h"
}

%{	
	#include "global.h"
	#include <fstream>
	extern FILE *yyin; 
%} 

%union {
int val;
Symbol* symbol;
}

%token ID
%token NUM
%token NOT
%token MULOP
%token SIGN
%token OR
%token RELOP
%token ASSIGNOP
%token IF
%token THEN
%token ELSE
%token WHILE
%token DO
%token TBEGIN
%token END
%token FUNCTION
%token PROCEDURE
%token VAR
%token INTEGER
%token REAL
%token ARRAY
%token OF
%token PROGRAM
%token DOTDOT


%token JUMP
%token LABEL
%token MINUS
%token PLUS
%token REALTOINT
%token INTTOREAL
%token MUL
%token DIV
%token AND
%token MOD
%token EQ
%token NE
%token LT
%token GT
%token LE
%token GE
%token JEQ
%token JGE
%token MOV
%token READ
%token WRITE
%token ENTER
%token LEAVE
%token INCSP
%token CALL
%token PUSH


%type <symbol> ID NUM expression variable simple_expression term factor 
%type <val> INTEGER REAL standard_type type MULOP ARRAY RELOP SIGN MUL DIV AND MOD

%%
program:		
PROGRAM
	{
		TablicaSymboli* tablica = TablicaSymboli::getInstance();
		tablica->rozpocznijDeklaracje();
	}
ID '(' identifier_list ')' ';'
	{
		cout<<"ID '(' identifier_list ')' ';'"<<endl;
		((Symbol*)$3)->setTypSymbolu(Symbol::FUNCKJA);
		((Symbol*)$3)->log();
		Quadruples* quadruples = Quadruples::getInstance();
		Quadruple* jump = new Quadruple(JUMP, 0, 0, "program");
		quadruples->skok(jump);
	}
declarations
	{
		//TODO:Sprawdzic czy potrzebne
		TablicaSymboli* tablica = TablicaSymboli::getInstance();
		tablica->zakonczDeklaracje();
	}
subprogram_declarations
{
	Quadruples* quadruples = Quadruples::getInstance();
	Quadruple* q1 = new Quadruple(LABEL,0,0);
	q1->setLabel("program");
	quadruples->wstaw(q1);
	
}
compound_statement
'.'
	{
		TablicaSymboli* tablica = TablicaSymboli::getInstance();
		tablica->zaallokujPamiecDlaZmiennychTymczasowych();
		ofstream program;
		string fileName = $3->getLexem()+".asm";
		program.open(fileName.c_str());
		Quadruples* quadruples = Quadruples::getInstance();
		program<<quadruples->getAsm();
		program<<"\texit";
		program.close();
	};

identifier_list:	
ID
	{
		cout<<"identifier_list: ID"<<endl;
		//Trafiamy z pierwszym napotkanym ID dla produkcji identifier_list
		TablicaSymboli* tablica = TablicaSymboli::getInstance();
		//Czyscimy wektor symboli bo rozpoczynamy identifier_list
		tablica->wyczyscAktualneID();
		tablica->dodajDoAktualnychID($1);
	}
|identifier_list ',' ID
{
	cout<<"identifier_list: identifier_list , ID"<<endl;
	//Trafiamy z kazdym nie pierwszym ID dla identifier_list
	TablicaSymboli* tablica = TablicaSymboli::getInstance();
	tablica->dodajDoAktualnychID($3);
	
};
declarations:		
declarations VAR identifier_list ':' type ';'
	{
		cout<<"declarations : declarations VAR identifier_list : type ;"<<endl;
		TablicaSymboli* tablica = TablicaSymboli::getInstance();
		Symbol::TYP_ZMIENNEJ typListy;
		if($5==INTEGER){
			typListy = Symbol::INTEGER;
		}else if ($5==REAL){
			typListy = Symbol::REAL;
		}else{
			typListy = Symbol::ARRAY;
		}
		//dla wszystkich ID z identifier_list ustawiamy typZmiennej rownowazny type
		tablica->ustawTypDlaAktualnychID(typListy);
		tablica->zaallokujPamiecDlaAktualnychID();
		//TODO:Obsluga ewentualnych bledow
		
	}
|;

type:			
standard_type 
	{
		cout<<"type: standard_type"<<endl;
	}
|ARRAY '[' NUM DOTDOT NUM ']' OF standard_type
	{
		cout<<"type: ARRAY '[' NUM DOTDOT NUM ']' OF standard_type"<<endl;
		//TODO: Operacje dla tablicy : indeksy, typ tablicy
	};

standard_type:
INTEGER 
|REAL;

subprogram_declarations: 
subprogram_declarations subprogram_declaration ';' 
|;

subprogram_declaration:	
subprogram_head 
	{
		cout<<"subprogram_declaration: subprogram_head<< declarations compound_statement"<<endl;
	}
declarations compound_statement
	{
		cout<<"subprogram_declaration: subprogram_head declarations compound_statement<<"<<endl;
		TablicaSymboli* tablica = TablicaSymboli::getInstance();
		Quadruples* quadruples = Quadruples::getInstance();
		quadruples->zakonczFunkcje();
		tablica->zaallokujPamiecDlaZmiennychTymczasowych();
		tablica->zamknijZakres();
		
	};

subprogram_head:	
FUNCTION
	{
		cout<<"subprogram_head: FUNCTION<< ID arguments ':' standard_type ';'"<<endl;
		TablicaSymboli* tablica = TablicaSymboli::getInstance();
		cout<<"\tCzyszcze wektor symboli"<<endl;
		tablica->wyczyscAktualneID();
		cout<<"\tRozpoczynam sekcje deklaracji"<<endl;
		tablica->rozpocznijDeklaracje();
	}
ID
	{
		cout<<"subprogram_head: FUNCTION ID<< arguments ':' standard_type ';'"<<endl;
		TablicaSymboli* tablica = TablicaSymboli::getInstance();
		cout<<"\tOtwieram zakres"<<endl;
		tablica->otworzZakres();
	}
arguments ':' standard_type ';'
	{
		cout<<"subprogram_head: FUNCTION ID arguments ':' standard_type ';'<<"<<endl;
		TablicaSymboli* tablica = TablicaSymboli::getInstance();
		Quadruples* quadruples = Quadruples::getInstance();
		Symbol::TYP_ZMIENNEJ typZwracanyFunkcji;
		if($7==INTEGER){
			typZwracanyFunkcji = Symbol::INTEGER;
		}else{
			typZwracanyFunkcji = Symbol::REAL;
		}
		tablica->deklarujFunkcje($3,typZwracanyFunkcji);
		quadruples->rozpocznijFunkcje($3);
	}
|PROCEDURE ID
	{
		cout<<"subprogram_head: PROCEDURE ID<< arguments ';'"<<endl;
		TablicaSymboli* tablica = TablicaSymboli::getInstance();
		cout<<"Otwieram zakres"<<endl;
		tablica->otworzZakres();
		
		
	}
arguments ';'
	{
		cout<<"subprogram_head: PROCEDURE ID arguments ';'<<"<<endl;
		TablicaSymboli* tablica = TablicaSymboli::getInstance();
		Quadruples* quadruples = Quadruples::getInstance();
		tablica->deklarujProcedure($2);
		quadruples->rozpocznijFunkcje($2);
	};

arguments:		
'('parameter_list')' 
|;

parameter_list:		
identifier_list ':' type
	{
		cout<<"parameter_list: identifier_list ':' type"<<endl;
		TablicaSymboli* tablica = TablicaSymboli::getInstance();
		//Rozpoczynamy sekcje nowych parametrow
		tablica->wyczyscAktualneParametry();
		Symbol::TYP_ZMIENNEJ typ;
		if($3 == INTEGER){
			typ = Symbol::INTEGER;
		}else if($3 == REAL){
			typ = Symbol::REAL;
		}else{
			typ = Symbol::ARRAY;
		}
		tablica->ustawTypDlaAktualnychID(typ);
		tablica->dodajAktualneIdDoParametrow();
	}
|parameter_list ';' identifier_list ':' type
	{
		cout<<"parameter_list: parameter_list ';' identifier_list ':' type"<<endl;
		TablicaSymboli* tablica = TablicaSymboli::getInstance();	
		Symbol::TYP_ZMIENNEJ typ;
		if($5 == INTEGER){
			typ = Symbol::INTEGER;
		}else if($5 == REAL){
			typ = Symbol::REAL;
		}else{
			typ = Symbol::ARRAY;
		}
		tablica->ustawTypDlaAktualnychID(typ);
		tablica->dodajAktualneIdDoParametrow();
	};

compound_statement:	
TBEGIN optional_statements END;

optional_statements:
statement_list
|;

statement_list:
statement
|statement_list ';' statement;

statement:		
variable ASSIGNOP expression
	{
		cout<<"statement: variable ASSIGNOP expression"<<endl;
		Quadruples* quadruples = Quadruples::getInstance();
		quadruples->przypisz($1,$3);
	}
|procedure_statement
	{
			cout<<"statement: procedure_statement"<<endl;
	}
|compound_statement
	{
			cout<<"statement: compound_statement"<<endl;
	}
|IF expression 
	{
			cout<<"statement: IF expression<< THEN statement ELSE statement"<<endl;
			Quadruples* quadruples = Quadruples::getInstance();
			TablicaSymboli* tablica = TablicaSymboli::getInstance();
			string label = quadruples->stworzLabelDlaIf();
			Symbol* zero = tablica->insert("0",NUM);
			quadruples->skok(new Quadruple(JEQ, $2, zero, label));
	}
THEN statement 
	{
			cout<<"statement: IF expression THEN statement<< ELSE statement"<<endl;
			Quadruples* quadruples = Quadruples::getInstance();
			string label = quadruples->stworzLabelDlaThen();
			quadruples->skok(new Quadruple(JUMP,0,0,label));
	}
ELSE 
	{
			cout<<"statement: IF expression THEN statement ELSE<< statement"<<endl;
			Quadruples* quadruples = Quadruples::getInstance();
			string labelKonca = quadruples->getLabelDlaIf();
			Quadruple* q = new Quadruple(LABEL,0,0);
			q->setLabel(labelKonca);
			quadruples->wstaw(q);
	}
statement
	{
			cout<<"statement: IF expression THEN statement ELSE statement<<"<<endl;
			Quadruples* quadruples = Quadruples::getInstance();
			string labelKonca = quadruples->getLabelDlaThen();
			Quadruple* q = new Quadruple(LABEL,0,0);
			q->setLabel(labelKonca);
			quadruples->wstaw(q);
	}
|WHILE
	{
			cout<<"statement: WHILE<< expression DO statement"<<endl;
			Quadruples* quadruples = Quadruples::getInstance();
			string labelPetli = quadruples->stworzLabelDlaWhile();
			Quadruple* q = new Quadruple(LABEL,0,0);
			q->setLabel(labelPetli);
			quadruples->wstaw(q);
	}
expression
	{
			cout<<"statement: WHILE expression<< DO statement"<<endl;
			Quadruples* quadruples = Quadruples::getInstance();
			TablicaSymboli* tablica = TablicaSymboli::getInstance();
			string label = quadruples->stworzLabelDlaWhile();
			Symbol* zero = tablica->insert("0",NUM);
			quadruples->skok(new Quadruple(JEQ, $3, zero, label));

	}
DO statement
	{
			cout<<"statement: WHILE expression DO statement<<"<<endl;
			Quadruples* quadruples = Quadruples::getInstance();
			//istotna kolejnosc, zdejmujemy przeciwnie niz wladalismy
			string labelKonca = quadruples->getLabelDlaWhile();
			string labelPoczatku = quadruples->getLabelDlaWhile();
			
			quadruples->skok(new Quadruple(JUMP, 0, 0, labelPoczatku));
			Quadruple* q = new Quadruple(LABEL,0,0);
			q->setLabel(labelKonca);
			quadruples->wstaw(q);
	};

variable:
ID 
	{
		cout<<"variable : ID"<<endl;
	}
|ID '['expression']'
	{
		cout<<"variable : ID '['expression']'"<<endl;
	};

procedure_statement:
ID 
	{
		cout<<"procedure_statement: ID"<<endl;
		Quadruples* quadruples = Quadruples::getInstance();
		TablicaSymboli* tablica = TablicaSymboli::getInstance();
		tablica->wyczyscWektorWyrazen();
		Symbol* funkcja = tablica->sprawdzPoprawnoscWywolania($1);
		quadruples->wywolaj(funkcja, tablica->pobierzWektorParametrowFunkcji(funkcja), tablica->pobierzWektorWyrazen());
	}
|ID '('expression_list')'
	{
		cout<<"procedure_statement: ID '('expression_list')'"<<endl;
		Quadruples* quadruples = Quadruples::getInstance();
		TablicaSymboli* tablica = TablicaSymboli::getInstance();
		Symbol* funkcja = tablica->sprawdzPoprawnoscWywolania($1);
		quadruples->wywolaj(funkcja, tablica->pobierzWektorParametrowFunkcji(funkcja), tablica->pobierzWektorWyrazen());
	};

expression_list:	
expression
	{
	cout<<"expression_list: expression"<<endl;
	TablicaSymboli* tablica = TablicaSymboli::getInstance();
	tablica->wyczyscWektorWyrazen();
	tablica->wstawDoWektoraWyrazen($1);
	}
|expression_list ',' expression
	{
		cout<<"expression_list: expression_list ',' expression"<<endl;
		TablicaSymboli* tablica = TablicaSymboli::getInstance();
		tablica->wstawDoWektoraWyrazen($3);
	};

expression:
simple_expression 
	{
		cout<<"expression: simple_expression"<<endl;
	}
|simple_expression RELOP simple_expression
	{
		cout<<"expression: simple_expression RELOP simple_expression"<<endl;
		Quadruples* quadruples = Quadruples::getInstance();
		$$ = quadruples->porownaj($2,$1,$3);
	};

simple_expression:	
term
	{
		cout<<"simple_expression: term"<<endl;
	}
|SIGN term
	{
		cout<<"simple_expression: SIGN term"<<endl;
		Quadruples* quadruples = Quadruples::getInstance();
		TablicaSymboli* tablica = TablicaSymboli::getInstance();
		if($1==MINUS){
			Symbol* minus = tablica->insert("-1",NUM);
			$$ = quadruples->pomnoz(MUL,$2,minus);
		}else{
			$$=$2;
		}
	}
|simple_expression SIGN term 
	{
		cout<<"simple_expression: simple_expression SIGN term"<<endl;
		Quadruples* quadruples = Quadruples::getInstance();
		$$ = quadruples->dodaj($2,$1,$3);
	}
|simple_expression OR term
	{
		cout<<"simple_expression: simple_expression OR term"<<endl;
	};

term:			
factor
	{
		cout<<"term : factor"<<endl;
	}
|term MULOP factor
	{
		cout<<"term : term MULOP factor"<<endl;
		Quadruples* quadruples = Quadruples::getInstance();
		$$ = quadruples->pomnoz($2,$1,$3);
	};

factor:			
variable
	{
		cout<<"factor : variable"<<endl;	
	}
|ID '('expression_list')'
	{
		cout<<"factor : ID '('expression_list')'"<<endl;	
		Quadruples* quadruples = Quadruples::getInstance();
		TablicaSymboli* tablica = TablicaSymboli::getInstance();
		Symbol* funkcja = tablica->sprawdzPoprawnoscWywolania($1);
		quadruples->wywolaj(funkcja, tablica->pobierzWektorParametrowFunkcji(funkcja), tablica->pobierzWektorWyrazen());
	}
|NUM	
	{
		cout<<"factor : NUM"<<endl;
	}
|'('expression')'
	{
		cout<<"factor : '('expression')'"<<endl;	
		$$=$2;
	}
|NOT factor
	{
		cout<<"factor : NOT factor"<<endl;
	};
%%

int main(int argc, char** argv){
	FILE* plik = fopen(argv[1],"r");
	yyin=plik;
	yyparse();
	fclose(plik);
	exit (0);
}
 
int yyerror(char const* s) 
{ 
printf("%s at line %d\n", s, lineno); 
return 1; 
}

